﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Linq;

namespace LaoLuo
{
    /// <summary>
    /// provides static methods for custom biz objects
    /// </summary>
    public static class CBO
    {
        /// <summary>
        /// get a list from a reader, then the reader is disposed.
        /// </summary>
        /// <typeparam name="T">the type of wanted object, it can be a value-type, but that will lead to box-instructions</typeparam>
        /// <param name="source">reader as the source</param>
        /// <returns>a list of objects, each of which contains data get from the datareader</returns>
        public static IList<T> FillCollection<T>(IDataReader source)
        {
            var ret = new List<T>();

            using (source)
            {
                while (source.Read())
                {
                    ret.Add(FillObject<T>(source, false));
                }
            }

            return ret;
        }

        /// <summary>
        /// get a object from a reader, then the reader is disposed.
        /// </summary>
        /// <typeparam name="T">the type of wanted object, it can be a value-type, but that will lead to box-instruction</typeparam>
        /// <param name="source">reader as the source</param>
        /// <returns>a object that contains data get from the datareader</returns>
        public static T FillObject<T>(IDataReader source)
        {
            using (source)
            {
                return FillObject<T>(source, true);
            }
        }

        /// <summary>
        /// get a object from a NameValueCollection
        /// </summary>
        /// <typeparam name="T">the type of wanted object, it can be a value-type, but that will lead to box-instruction</typeparam>
        /// <param name="source">NameValueCollection as the source</param>
        /// <returns>a object that contains data get from the NameValueCollection</returns>
        public static T FillObject<T>(NameValueCollection source)
        {
            var ret = Activator.CreateInstance<T>() as object;
            foreach (var propertyInfo in PropertyContainer.GetPropertiesInstancePublic<T>())
            {
                try
                {
                    if (string.IsNullOrEmpty(source[propertyInfo.Name]))
                    {
                        continue;
                    }
                    propertyInfo.SetValue(ret, TypeConverter.ToType(source[propertyInfo.Name], propertyInfo.PropertyType), null);
                }
                catch (IndexOutOfRangeException)
                {
                    // property name does not exist in the reader
                }
                catch(Exception ex)
                {
                    throw new InvalidOperationException("failed at " + propertyInfo.Name, ex);
                }
            }
            return (T)ret;
        }

        /// <summary>
        /// reader must be disposed outside
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="doRead"></param>
        /// <returns></returns>
        private static T FillObject<T>(IDataReader source, bool doRead)
        {
            if (doRead && !source.Read())
            {
                return default(T);
                //throw new InvalidOperationException("source can not be read");
            }

            var ret = Activator.CreateInstance<T>() as object;
            foreach (var propertyInfo in PropertyContainer.GetPropertiesInstancePublic<T>())
            {
                try
                {
                    if (source[propertyInfo.Name] == null)
                    {
                        continue;
                    }

                    var str = source[propertyInfo.Name].ToString();

                    if (string.IsNullOrEmpty(str))
                    {
                        continue;
                    }

                    propertyInfo.SetValue(ret, TypeConverter.ToType(str, propertyInfo.PropertyType), null);
                }
                catch (IndexOutOfRangeException)
                {
                    // property name does not exist in the reader
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("failed at " + propertyInfo.Name, ex);
                }
            }
            return (T)ret;
        }
    }
}
